﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

[assembly: TagPrefix("CodeGecko.SocialControl", "sc")]
namespace CodeGecko.SocialControl {
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:AddThis runat=\"server\"></{0}:AddThis>")]
    [ParseChildren(true, "Text")]
    [ToolboxBitmap(typeof(ShareThis), "AspNetSocialControls.ico")]
    [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
    public sealed class AddThis : BaseSocialControl {
        public AddThis() : base() {
            // Global properties - should be specified in config!

            GlobalSettings.AddProperty("publisher", new SimpleProperty<string>(Publisher));
            GlobalSettings.AddProperty("tracking", new SimpleProperty<string>(Tracking.ToString()));
            GlobalSettings.AddProperty("send_services", new SimpleProperty<string>(SendServices));
            GlobalSettings.AddProperty("exclusive_service", new SimpleProperty<string>(ExclusiveService));
            GlobalSettings.AddProperty("headerTitle", new SimpleProperty<string>(HeaderTitle));
            GlobalSettings.AddProperty("headerfg", new SimpleProperty<string>(HeaderForeground));
            GlobalSettings.AddProperty("headerbg", new SimpleProperty<string>(HeaderBackground));
            GlobalSettings.AddProperty("offsetLeft", new SimpleProperty<string>(OffsetLeft.ToString(CultureInfo.InvariantCulture)));
            GlobalSettings.AddProperty("offsetTop", new SimpleProperty<string>(OffsetTop.ToString(CultureInfo.InvariantCulture)));
            GlobalSettings.AddProperty("onhover", new SimpleProperty<string>(Hover.ToString()));
            GlobalSettings.AddProperty("autoclose", new SimpleProperty<string>(AutoClose.ToString()));
            GlobalSettings.AddProperty("autoPosition", new SimpleProperty<string>(AutoPosition.ToString()));
            GlobalSettings.AddProperty("embeds", new SimpleProperty<string>("false"));
            GlobalSettings.AddProperty("doneScreen", new SimpleProperty<string>("false"));

            // Individual properties
            AddProperty("displayText", new SimpleProperty<string>(Text));
            AddProperty("st_title", new SimpleProperty<string>(String.Empty));
            AddProperty("st_url", new SimpleProperty<string>(String.Empty));
        }
        
        public class Helpers {
            public enum ButtonModes { chicklet, custom, button, hcount, vcount, pcount, large }
            public enum ChickletServices { aim, bebo, blinklist, blogger, businessweek, care2, current, dealsplus, delicious, digg, dilgo,
                facebook, fark, faves, fresqui, friendfeed, funp, gbuzz, google_bmarks, kirsty, linkedin, meaneame, messenger,
                mister_wong, mixx, myspace, n4g, newsvine, oknotizie, propeller, reddit, simpy, sharethis, slashdot, sonico,
                sphinn, stumbleupon, technorati, twackle, twine, twitter, windows_live, xanga, yahoo_bmarks, ybuzz, yigg
            }
            public enum AutoPosition { Center, True, False }
        }

        protected override string ControlName { get { return "ShareThis"; } }

        #region Control global properties - Behaviour

        [Bindable(true)]
        [Category("Behaviour")]
        [DefaultValue(Helpers.ButtonModes.button)]
        [Localizable(true)]
        public Helpers.ButtonModes ButtonMode {
            get { return (ViewState["ButtonMode"] == null) ? Helpers.ButtonModes.button: (Helpers.ButtonModes)ViewState["ButtonMode"]; }
            set { ViewState["ButtonMode"] = value; }
        }

        [Bindable(true)]
        [Category("Behaviour")]
        [DefaultValue(Helpers.ChickletServices.sharethis)]
        [Localizable(true)]
        public Helpers.ChickletServices Service {
            get { return (ViewState["Service"] == null) ? Helpers.ChickletServices.sharethis : (Helpers.ChickletServices)ViewState["Service"]; }
            set { ViewState["Service"] = value; }
        }

        [Bindable(true)]
        [Category("Behaviour")]
        [DefaultValue("")]
        [Localizable(true)]
        public String Url {
            get { return ((ViewState["Url"] == null) ? this.Page.Request.Url.AbsolutePath : (String)ViewState["Url"]); }
            set { ViewState["Url"] = value; this.SetValue<string>("st_url", value.ToString()); }
        }

        [Bindable(true)]
        [Category("Behaviour")]
        [DefaultValue("")]
        [Localizable(true)]
        public String Title {
            get { return ((ViewState["Title"] == null) ? this.Page.Title : (String)ViewState["Url"]); }
            set { ViewState["Url"] = value; this.SetValue<string>("st_title", value.ToString()); }
        }

        [Bindable(true)]
        [Category("Behaviour")]
        [DefaultValue(true)]
        [Localizable(true)]
        public Boolean Hover {
            get { return ((ViewState["Hover"] == null) ? true : (Boolean)ViewState["Hover"]); }
            set { ViewState["Hover"] = value; this.GlobalSettings.SetValue<string>("onhover", value.ToString()); }
        }

        [Bindable(true)]
        [Category("Behaviour")]
        [DefaultValue(true)]
        [Localizable(true)]
        public Boolean AutoClose
        {
            get { return ((ViewState["AutoClose"] == null) ? true : (Boolean)ViewState["AutoClose"]); }
            set { ViewState["AutoClose"] = value; this.GlobalSettings.SetValue<string>("autoclose", value.ToString()); }
        }

        [Bindable(true)]
        [Category("Behaviour")]
        [DefaultValue(true)]
        [Localizable(true)]
        public Helpers.AutoPosition AutoPosition {
            get { return ((ViewState["AutoPosition"] == null) ? Helpers.AutoPosition.False: (Helpers.AutoPosition)ViewState["AutoPosition"]); }
            set { ViewState["AutoPosition"] = value; this.GlobalSettings.SetValue<string>("autoPosition", value.ToString()); }
        }

        #endregion

        #region Control global properties - Appearance

        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("ShareThis")]
        [Localizable(true)]
        public String HeaderTitle {
            get { String s = (String)ViewState["HeaderTitle"]; return ((s == null) ? this.ControlName : s); }
            set { ViewState["HeaderTitle"] = value; this.GlobalSettings.SetValue<string>("headerTitle", value); }
        }

        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("#FFFFFF")]
        [Localizable(true)]
        public String HeaderForeground {
            get { return (String.IsNullOrEmpty((String)ViewState["HeaderForeground"])) ? "#FFFFFF" : (String)ViewState["HeaderForeground"]; }
            set { Color c = ColorTranslator.FromHtml(value); ViewState["HeaderForeground"] = ColorTranslator.ToHtml(c); this.GlobalSettings.SetValue<string>("headerfg", Convert.ToString(ColorTranslator.ToHtml(c))); }
        }

        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("#CCCCCC")]
        [Localizable(true)]
        public String HeaderBackground {
            get { return (String.IsNullOrEmpty((String)ViewState["HeaderBackground"])) ? "#CCCCCC" : (String)ViewState["HeaderBackground"]; }
            set { Color c = ColorTranslator.FromHtml(value); ViewState["HeaderBackground"] = ColorTranslator.ToHtml(c); this.GlobalSettings.SetValue<string>("headerbg", Convert.ToString(ColorTranslator.ToHtml(c))); }
        }

        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue(0)]
        [Localizable(true)]
        public Int32 OffsetLeft {
            get { return (ViewState["OffsetLeft"] == null) ? 0 : (Int32)ViewState["OffsetLeft"]; }
            set { ViewState["OffsetLeft"] = value; this.GlobalSettings.SetValue<string>("offsetLeft", value.ToString()); }
        }

        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue(0)]
        [Localizable(true)]
        public Int32 OffsetTop {
            get { return (ViewState["OffsetTop"] == null) ? 0 : (Int32)ViewState["OffsetTop"]; }
            set { ViewState["OffsetTop"] = value; this.GlobalSettings.SetValue<string>("offsetTop", value.ToString()); }
        }

        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("ShareThis")]
        [Localizable(false)]
        public String Text {
            get { return (ViewState["Text"] == null) ? "ShareThis" : (String)ViewState["Text"]; }
            set { ViewState["Text"] = value; this.SetValue<string>("displayText", value); }
        }

        #endregion

        #region Control global properties - Settings

        [Bindable(true)]
        [Category("Settings")]
        [DefaultValue("")]
        [Localizable(false)]
        public String Publisher {
            get { return (ViewState["Publisher"] == null) ? String.Empty: (String)ViewState["Publisher"]; }
            set { ViewState["Publisher"] = value; this.GlobalSettings.SetValue<string>("publisher", value); }
        }

        [Bindable(true)]
        [Category("Settings")]
        [DefaultValue("")]
        [Localizable(false)]
        public String SendServices {
            get { return (ViewState["SendServices"] == null) ? String.Empty : (String)ViewState["SendServices"]; }
            set { ViewState["SendServices"] = value; this.GlobalSettings.SetValue<string>("send_services", value); }
        }

        [Bindable(true)]
        [Category("Settings")]
        [DefaultValue("")]
        [Localizable(false)]
        public String ExclusiveService {
            get { return (ViewState["ExclusiveService"] == null) ? String.Empty : (String)ViewState["ExclusiveServices"]; }
            set { ViewState["ExclusiveService"] = value; this.GlobalSettings.SetValue<string>("exclusive_service", value); }
        }

        [Bindable(true)]
        [Category("Settings")]
        [DefaultValue(true)]
        [Localizable(false)]
        public Boolean Tracking {
            get { Boolean s = (ViewState["Tracking"] != null) ? (Boolean)ViewState["Tracking"] : true; return s; }
            set { ViewState["Tracking"] = Convert.ToBoolean(value); this.GlobalSettings.SetValue<string>("tracking", Convert.ToString(value)); }
        }
        
        #endregion

        public override string RegisterGlobalSettings() {
            StringBuilder sb = new StringBuilder("{");
            foreach (string s in this.GlobalSettings.Properties.Keys) {
                SimpleProperty<string> p = (SimpleProperty<string>)this.GlobalSettings.Properties[s];
                Boolean b;
                if (Boolean.TryParse(p.Value.ToString(), out b)) {
                    sb.Append(String.Concat(s, ": ", b.ToString().ToLowerInvariant(), ","));
                } else {
                    Int32 i;
                    if (Int32.TryParse(p.Value.ToString(), out i)) {
                        sb.Append(String.Concat(s, ": ", i.ToString(), ","));
                    } else {
                        sb.Append(String.Concat(s, ": '", p.Value.ToString(), "',"));
                    }
                }
            }
            sb.Remove(sb.Length - 1, 1);
            sb.Append("}");
            return String.Format("stLight.options({0});", sb.ToString());
        }
        protected override string LoaderUrl { get { return @"http://w.sharethis.com/button/buttons.js"; } }

        /// <summary>
        /// Renders the control at runtime
        /// </summary>
        /// <param name="output">The HtmlTextWriter to which to write the control's output.</param>
        public override void RenderControl(HtmlTextWriter writer) {
            /* CSS class for our control has to be set before calling the render,
             * so this is a perfect place to choose it.
             */
            switch (this.ButtonMode) {
                case Helpers.ButtonModes.chicklet:
                    this.CssClass = String.Concat("st_", this.Service.ToString());
                    break;
                case Helpers.ButtonModes.custom:
                case Helpers.ButtonModes.hcount:
                case Helpers.ButtonModes.pcount:
                case Helpers.ButtonModes.vcount:
                case Helpers.ButtonModes.large:
                    this.CssClass = String.Format("st_{0}_{1}",
                        ((String.IsNullOrEmpty(this.Service.ToString())) ? "sharethis" : this.Service.ToString()),
                        this.ButtonMode.ToString());
                    break;
                default:
                    this.CssClass = "st_sharethis";
                    break;
            }
            base.RenderControl(writer);
        }

        /// <summary>
        /// Renders the contents of the control at runtime
        /// </summary>
        /// <param name="output">The HtmlTextWriter to which to write the control's output.</param>
        protected override void RenderContents(HtmlTextWriter output) {
            // Build ShareThis per-control parameters
            StringBuilder sb = new StringBuilder();
            sb.Append(String.Format("var {0} = document.getElementById('{0}');", this.ClientID));
            foreach (string t in this.Properties.Keys) {
                /* ShareThis uses <element>.setAttribute(key, value) in Javascript to add per-item attributes.
                 * Page.ClientScript.RegisterExpandoAttribute uses Javascript prototyping (<element>.key = value),
                 * which can't be retrieved with setAttribute, hence the requirement for a StringBuilder - otherwise, we'd use this:
                 * 
                 * Page.ClientScript.RegisterExpandoAttribute(this.ClientID, t, sp.Value.ToString());
                 * 
                 * TODO: Petition ShareThis to stop being shit Javascript programmers and start using jQuery.
                 */

                SimpleProperty<string> sp = (SimpleProperty<string>)this.Properties[t];
                sb.Append(String.Format("{0}.setAttribute('{1}','{2}');", this.ClientID, t, sp.Value.ToString()));                
            }

            /* ShareThis over-rides all your lovely CSS using some more <element>.setAttribute('style', '<CSS here>') magic.
             * This is a bit pants really - for a servercontrol, we should be able to use the ASP.NET properties provided for us for styling.
             * So that's exactly what we'll do, by calling GetStyleAttributes, to get a collection of CSS props (auto-gen'd by ASP.NET from our control properties).
             * CodeGecko 1, ShareThis 0. :-)
             */
            CssStyleCollection cs = this.ControlStyle.GetStyleAttributes(this);

            /* Oh, ShareThis does one more annoying thing - add 3px margin either side of the child span.
             * Margin is a pain-in-the-ass when you want to style something properly. So, we'll replace it with padding instead.
             */
            cs.Add("margin", "0px");
            cs.Add("padding", "0px 3px");

            StringBuilder sbf = new StringBuilder();
            foreach (string s in cs.Keys) sbf.Append(String.Concat(s, ":", cs[s], ";"));

            /* Javascript to over-ride ShareThis default settings with CSS applied at control level
             * Has to use setTimeout to detect:
             *  a) when the child <span> has been created by ShareThis (surprise surprise, there's no event-driven model or hooks);
             *  b) when the child <span> has had setAttribute() called on it to set the default ShareThis styles -
             *      otherwise our styles will be set before the ShareThis defaults, subsequently being overwritten.
             *  Oh, and I know the child span has a class called stButton, but Javascript-applied properties WILL override your beautifully-written CSS anyway -
             *  that's the *cascading* bit. So this, unfortunately, *is* the only solution.
             */
            sb.AppendFormat("function {0}_f(){{if(({0}.childNodes.length>0)&&({0}.childNodes[0].getAttribute('style').length>0)){{{0}.childNodes[0].setAttribute('style','{1}');}}else{{setTimeout(\"{0}_f()\", 100);}}}}{0}_f();",
                this.ClientID,
                sbf.ToString());
            // if (!Page.ClientScript.IsClientScriptIncludeRegistered("ShareThisLoader")) Page.ClientScript.RegisterClientScriptInclude("ShareThisLoader", this.LoaderUrl);
            Page.ClientScript.RegisterStartupScript(this.GetType(), String.Format("{0}instanceconfig", this.ClientID), sb.ToString(), true);

            if (this.ButtonMode == Helpers.ButtonModes.custom) output.Write(this.Text);
        }

    }
}
